using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Magiq.Sql.Tests.TestModel;
using Magiq.Tests.Support;
using Magiq.Update;
using NUnit.Framework;

namespace Magiq.Sql.Tests {
  [TestFixture]
  public class CompositeKeysTest {
    #region Setup/Teardown

    [SetUp]
    public void SetUp() {
      MagiqToSqlTestConfigurator.Current.SetUp();
      domain = new CompositePkTestDomain();
      parents = null;
      children = MagiqToSqlTestConfigurator.Current.PersistAndGet(domain.Children);
    }

    [TearDown]
    public void TearDown() {
      MagiqToSqlTestConfigurator.Current.TearDown();
    }

    #endregion

    private IQueryable<CompositePkParent> Parents {
      get {
        if (parents == null)
          parents = MagiqToSqlTestConfigurator.Current.PersistAndGet(domain.Parents);
        return parents;
      }
    }

    private IQueryable<CompositePkChild> children;
    private CompositePkTestDomain domain;
    private IQueryable<CompositePkParent> parents;

    private static void TestDelete<T>(Expression<Func<T, bool>> where, IQueryable<T> items) where T : class {
      var count = items.Where(where).Count();
      items.Where(where).Delete().Should(Be.EqualTo(count));
      items.Where(where).Count().Should(Be.EqualTo(0));
    }

    private static void TestUpdate<T>(Expression<Func<T, bool>> where, Func<IQueryable<T>, IUpdate<T>> set,
                                      IQueryable<T> items) {
      TestUpdate(where, x => x.Count(), set, items);
    }

    private static void TestUpdate<T>(Expression<Func<T, bool>> where, Func<IEnumerable<T>, int> getCount,
                                      Func<IQueryable<T>, IUpdate<T>> set, IQueryable<T> items) {
      var count = getCount(items.Where(where));
      set(items.Where(where)).Update().Should(Be.EqualTo(count));
    }

    private static void TestInsert<T>(Expression<Func<T, bool>> where, Expression<Func<T, T>> insert,
                                      IQueryable<T> items) where T : class {
      var totalCount = items.Count();
      var count = items.Where(where).Count();
      items.Where(where).Insert(insert).Into(items);
      items.Count().Should(Be.EqualTo(totalCount + count));
    }

    [Test]
    public void ShouldDeleteEntityWithCompositePrimaryKey() {
      TestDelete(x => true, children);
    }

    [Test]
    public void ShouldDeleteWithWhereContainingAnAssociationWithCompositePrimaryKey() {
      TestDelete(x => x.Child.Prop == "test1", Parents);
    }

    [Test]
    public void ShouldInsertAssociationWithCompositePrimaryKey() {
      TestInsert(x => true, x => new CompositePkParent
                                   {
                                     Prop = "bbla",
                                     Child = x.Child
                                   }, Parents);
    }

    [Test]
    public void ShouldInsertEntityWithCompositePrimaryKey() {
      TestInsert(x => true, x => new CompositePkChild
                                   {
                                     Id1 = x.Id1 + 1000,
                                     Id2 = x.Prop,
                                     Prop = "lala"
                                   }, children);
    }

    [Test]
    public void ShouldInsertWithWhereContainingAnAssociationWithCompositePrimaryKey() {
      TestInsert(x => x.Child.Prop == "test1", x => new CompositePkParent
                                                      {
                                                        Prop = "lala"
                                                      }, Parents);
    }

    [Test]
    public void ShouldUpdateAssociationEntityWithCompositePrimaryKey() {
      TestUpdate(x => true, x => x.Where(u => u.Child != null).Select(u => u.Child).Count(),
                 s => s.Set(x => x.Child.Prop, "lele"), Parents);
    }

    [Test]
    public void ShouldUpdateAssociationWithCompositePrimaryKey() {
      TestUpdate(x => true,
                 s => s.Set(x => x.Child, x => x.Child),
                 Parents);
    }

    [Test]
    public void ShouldUpdateEntityWithCompositePrimaryKey() {
      TestUpdate(x => true, s => s.Set(x => x.Prop, "lala"), children);
    }

    [Test]
    public void ShouldUpdateWithWhereContainingAnAssociationWithCompositePrimaryKey() {
      TestUpdate(x => x.Child.Prop == "test1", s => s.Set(x => x.Prop, "lala"), Parents);
    }
  }
}